package com.study.sequence.common;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Created by 张波 on 2020/5/23.
 */
@Slf4j
@Data
public class SequenceBuffer implements Serializable {

    private String name;
    /*业务键*/
    private String businessKey;
    /*高位值位数*/
    private int valueSize;
    /*高位值当前值*/
    private int currentValue;
    /*步长值*/
    private int stepValue;
    /*步长值位数*/
    private int stepValueSize;
    /*当前步长*/
    private AtomicLong currentStep;
    /*是否已刷新*/
    private AtomicBoolean flush;
    /*前缀字符串*/
    private String sequenceStr;

    private AtomicBoolean isInit;


    public SequenceBuffer() {
        this.businessKey = CommonConstract.DEFAULT_BUSINESS_KEY;
        currentStep = new AtomicLong(0);
        flush = new AtomicBoolean(false);
        isInit = new AtomicBoolean(false);
    }

    public SequenceBuffer(String name, String businessKey) {
        this.name = name;
        this.businessKey = businessKey;
        currentStep = new AtomicLong(0);
        flush = new AtomicBoolean(false);
        isInit = new AtomicBoolean(false);
    }

    /**
     * 初始化
     *
     * @param valueSize    序列位长度
     * @param currentValue 序列位值
     * @param stepValue    缓存位步长值
     */
    public void init(int valueSize, int currentValue, int stepValue, boolean flushFlag) {
        this.valueSize = valueSize;
        this.currentValue = currentValue;
        this.stepValue = stepValue;
        sequenceStr = StringUtils.leftPad(String.valueOf(this.currentValue), this.valueSize, "0");
        currentStep = new AtomicLong(0);
        flush.compareAndSet(false, flushFlag);
        stepValueSize = String.valueOf(this.stepValue).length() - 1;
        isInit = new AtomicBoolean(flushFlag);
    }

    /**
     * 刷新初始值
     *
     * @param currentValue
     */
    public void reset(int currentValue) {
        this.currentValue = currentValue;
        sequenceStr = StringUtils.leftPad(String.valueOf(this.currentValue), this.valueSize, "0");
        currentStep = new AtomicLong(0);
        updateFlush(false, true);
        isInit.compareAndSet(false, true);
    }

    /**
     * 在同步块中更新flush值，避免因为多线程请求量过高时反复切换
     *
     * @param source
     * @param target
     * @return
     */
    private synchronized boolean updateFlush(boolean source, boolean target) {
        return this.flush.compareAndSet(source, target);
    }

    /**
     * 生成带前缀字符串的序列号
     *
     * @param prefix
     * @return
     */
    public String getSequence(String prefix) {
        String sequence = getSequence();
        if (null == sequence) {
            return null;
        }
        if (StringUtils.isBlank(prefix)) {
            return sequence;
        }
        return prefix + getSequence();
    }

    /**
     * 生成不带前缀的序列号
     *
     * @return
     */
    public String getSequence() {
        if (!this.getIsInit().get()) {
            return null;
        }
        long step = currentStep.getAndIncrement();
        if (stepValue <= step) {
            /*如果当前值等于或者大于步长值，则说明已经失效了*/
            updateFlush(true, false);
            return null;
        }
        return sequenceStr + StringUtils.leftPad(String.valueOf(step), stepValueSize, "0");
    }

    /**
     * 是否达到极限需要刷新备用池
     *
     * @return
     */
    public boolean isFlus(float proportion) {
        if ((this.stepValue - currentStep.get()) <= (this.stepValue * proportion)) {
            /*如果当前数值余量 小于等于 传入的占比值，则说明触发了数值刷新条件*/
            return true;
        }
        return false;
    }

}
